package com.ruoyi.flow.common.utils;


import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * Created by Administrator on 2017/7/18.
 */
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String PATTERN_YEAR = "yyyy";
    public static final String PATTERN_MONTH = "MM";
    public static final String PATTERN_DAY = "dd";
    public static final String PATTERN_HOUR = "HH";
    public static final String PATTERN_MINUTE = "mm";
    public static final String PATTERN_SECOND = "ss";
    public static final String PATTERN_WEEK = "E";

    /**
     * 得到日期字符串 默认格式（yyyy-MM-dd） pattern可以为："yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String format(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        pattern = StringUtils.defaultIfEmpty(pattern, "yyyy-MM-dd");
        String formatDate = DateFormatUtils.format(date, pattern);
        return formatDate;
    }

    /**
     * 根据指定格式获取当前时间字符串
     *
     * @param pattern yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static String getCurrentTime(String pattern) {
        pattern = StringUtils.defaultIfEmpty(pattern, DEFAULT_PATTERN);
        return format(new Date(), pattern);
    }

    /**
     * 根据默认格式{@code yyyy-MM-dd HH:mm:ss}获取当前时间字符串
     *
     * @return
     */
    public static String getCurrentTime() {
        return getCurrentTime(DEFAULT_PATTERN);
    }

    /**
     * 获取当前时间的long型值(秒数)。
     *
     * @return long型日期
     */
    public static long getCurrentLongTime() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 日期型字符串转化为日期 格式
     * { "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm",
     * "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm",
     * "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm" }
     */
    public static Date parseDate(String dateStr) {
        checkNotNull(dateStr);
        try {
            return parseDate(dateStr, parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 获取过去的天数
     *
     * @param date
     * @return
     */
    public static long pastDays(Date date) {
        long t = new Date().getTime() - date.getTime();
        return t / (24 * 60 * 60 * 1000);
    }

    /**
     * 获取过去的小时
     *
     * @param date
     * @return
     */
    public static long pastHour(Date date) {
        long t = new Date().getTime() - date.getTime();
        return t / (60 * 60 * 1000);
    }

    /**
     * 获取过去的分钟
     *
     * @param date
     * @return
     */
    public static long pastMinutes(Date date) {
        long t = new Date().getTime() - date.getTime();
        return t / (60 * 1000);
    }

    /**
     * 获取两个日期之间相隔的日期
     *
     * @param before 开始日期
     * @param after  结束日期
     * @return
     */
    public static List<Date> getDaysBetweenTwoDate(Date before, Date after) {
        List<Date> days = new ArrayList<Date>();
        checkNotNull(before);
        checkNotNull(after);
        checkState(before.compareTo(after) < 1, "date before {} must be less than or equal date after {}.", before, after);
        if (isSameDay(before, after)) {
            return days;
        } else {
            Date tmpDate = addDays(before, 1);
            while (tmpDate.compareTo(after) == -1) {
                days.add(tmpDate);
                tmpDate = addDays(tmpDate, 1);
            }
        }
        return days;
    }

    /**
     * 获取两个日期之间间隔的月份
     *
     * @param before 开始日期
     * @param after  结束日期
     * @return yyyy-MM格式的字符串集合
     */
    public static List<String> getMonthsBetweenTwoDate(Date before, Date after) {
        checkNotNull(before, "null is not a before date value.");
        checkNotNull(after, "null is not a after date value.");
        checkState(before.compareTo(after) < 1, "date before {} must be less than or equal date after {}.", before, after);
        List<String> months = new ArrayList<String>();
        if (!isSameDay(before, after)) {
            Date tmpDate = addMonths(before, 1);
            while (tmpDate.compareTo(after) == -1) {
                months.add(format(tmpDate, "yyyy-MM"));
                tmpDate = addMonths(tmpDate, 1);
            }
        }
        return months;
    }

    /**
     * 获取两个日期之间间隔的周
     *
     * @param before
     * @param after
     * @return 返回 {@code Week}
     * @see Week
     */
    public static List<Week> getWeeksBetweenTwoDate(Date before, Date after) {
        checkNotNull(before, "null is not a before date value.");
        checkNotNull(after, "null is not a after date value.");
        checkState(before.compareTo(after) < 1, "date before {} must be less than or equal date after {}.", before, after);
        List<Week> weeks = new ArrayList<Week>();
        if (!isSameDay(before, after)) {
            Week week1 = getWeek(before);
            Week week2 = getWeek(after);
            Date tmpDate = addWeeks(before, 1);
            Week tmpWeek = getWeek(tmpDate);
            while (tmpWeek.compareTo(week2) == -1) {
                weeks.add(tmpWeek);
                tmpDate = addWeeks(tmpDate, 1);
                tmpWeek = getWeek(tmpDate);
            }
        }
        return weeks;
    }

    /**
     * 统计两个时间范围内的所有小时
     *
     * @param before
     * @param after
     * @return
     */
    public static List<String> getHoursBetweenTwoTime(Date before, Date after) {
        checkNotNull(before, "null is not a before date value.");
        checkNotNull(after, "null is not a after date value.");
        checkState(before.compareTo(after) < 1, "date before {} must be less than or equal date after {}.", before, after);
        List<String> hours = new ArrayList<String>();
        Date tmpDate = addHours(before, 1);
        while (tmpDate.compareTo(after) == -1) {
            hours.add(format(tmpDate, "yyyy-MM-dd HH"));
            tmpDate = addHours(tmpDate, 1);
        }
        return hours;
    }

    /**
     * 获取某个日期所属周
     *
     * @param d
     * @return
     */
    private static Week getWeek(Date d) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(d);
        int year = cal.get(Calendar.YEAR);
        // 如果所给日期所属月是12月，但是week=1，那么年需要+1
        int week = cal.get(Calendar.WEEK_OF_YEAR);
        if (cal.get(Calendar.MONTH) == Calendar.DECEMBER && week == 1) {
            year = year + 1;
        }
        return new Week(year, week);
    }

    /**
     * 获取两个日期之间的天数
     *
     * @param before 开始日期
     * @param after  结束日期
     * @return
     */
    public static long getDistanceOfTwoDate(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
    }

    /**
     * 根据年和周获取这周的第一天
     *
     * @param year
     * @param week
     * @return {@see Date }
     */
    public static Date getFirstDayOfWeek(int year, int week) {
        Week _week = new Week(year, week);
        return _week.getFirstDay();
    }

    /**
     * 根据年和周获取这周的最后一天
     *
     * @param year
     * @param week
     * @return
     */
    public static Date getLastDayOfWeek(int year, int week) {
        Week _week = new Week(year, week);
        return _week.getLastDay();
    }

    /**
     * 根据年和周获取本周的星期几的日期
     *
     * @param year 年
     * @param week 周
     * @param day  星期几，使用样例:{@code Calendar.MONDAY},从0开始
     * @return
     */
    public static Date getDayOfWeek(int year, int week, int day) {
        Week _week = new Week(year, week);
        return _week.getDayOfWeek(day);
    }

    /**
     * 获取某月第一天
     *
     * @param year
     * @param month
     * @return
     */
    public static Date getFirstDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 1);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 获取某月的最后一天
     *
     * @param year  年
     * @param month 月
     * @return
     */
    public static Date getLastDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 计算某个季度的开始月份
     *
     * @param quarter
     * @return
     */
    private static int getStartMonthOfQuarter(int quarter) {
        int[] month = {1, 4, 7, 10};
        return month[quarter - 1];
    }

    /**
     * 计算某个季度的开始月份
     *
     * @param quarter
     * @return
     */
    private static int getEndMonthOfQuarter(int quarter) {
        int[] month = {3, 6, 9, 12};
        return month[quarter - 1];
    }

    /**
     * 获取季度的第一天
     *
     * @param year
     * @param quarter
     * @return
     */
    public static Date getFirstDayOfQuarter(int year, int quarter) {
        Calendar cal = Calendar.getInstance();
        int month = getStartMonthOfQuarter(quarter);
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 1);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 获取季度的最后一天
     *
     * @param year
     * @param quarter
     * @return
     */
    public static Date getLastDayOfQuarter(int year, int quarter) {
        Calendar cal = Calendar.getInstance();
        int month = getEndMonthOfQuarter(quarter);
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 获取年的第一天
     *
     * @param year
     * @param formatStr
     * @return
     */
    public static Date getFirstDayOfYear(int year, String formatStr) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.DAY_OF_YEAR, cal.getMinimum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 1);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 获取年的最后一天
     *
     * @param year
     * @return
     */
    public static Date getLastDayOfYear(int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.DAY_OF_YEAR, cal.getMaximum(Calendar.DATE));
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        Date date = cal.getTime();
        return date;
    }

    /**
     * 转换long型(秒数)日期格式为字符型日期，精确到时分秒，如：convertTimeToString( 1264834129 ) -->2010-01-30 14:48:49
     *
     * @param longTime 需要转换的long型日期
     * @return 格式为yyyy-MM-dd HH:mm:ss的字符型日期
     */
    public static String convertTimeToString(long longTime) {
        return convertTimeToString(longTime, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 转换long型(秒数)日期格式为字符型日期，日期格式通过 format 定义，如：convertTimeToString( 1264834129 ,"yyyy-MM-dd" ) --> 2010-01-30
     *
     * @param longTime 需要转换的long型日期
     * @param format   日期格式 年：yyyy 月：MM 日：dd 小时：HH 分钟：mm 秒：ss
     * @return 格式为yyyy-MM-dd HH:mm:ss的字符型日期
     */
    public static String convertTimeToString(long longTime, String format) {
        try {
            Timestamp t = new Timestamp(longTime * 1000);
            SimpleDateFormat sDateFormat = new SimpleDateFormat(format);
            return sDateFormat.format(t);
        } catch (Exception ex) {
            throw new RuntimeException("Can't format the time by format[" + format + "]!");
        }

    }

    /**
     * 转换字符型日期(精确到时分秒)为long型日期(秒数)，如：convertTimeToLong( "2010-01-30 14:48:49" )--> 1264834129
     *
     * @param dateTime 需要转换的Str型日期
     * @return long型日期
     */
    public static long convertTimeToLong(String dateTime) {
        return convertTimeToLong(dateTime, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 转换字符型日期(日期格式通过format定义)为long型日期(秒数)，如：convertTimeToLong( "2010-01-30","yyyy-MM-dd" ) --> 1264780800
     *
     * @param dateTime 需要转换的Str型日期
     * @param format   日期格式 年：yyyy 月：MM 日：dd 小时：HH 分钟：mm 秒：ss
     * @return long型日期
     */
    public static long convertTimeToLong(String dateTime, String format) {
        long date = 0;
        try {
            Date d = parseDate(dateTime, format);
            date = d.getTime() / 1000;
        } catch (Exception ex) {
            throw new RuntimeException("Can't format the time by format[" + format + "]!");
        }
        return date;
    }

    public static class Week {

        private int year;
        private int week;

        public Week(int year, int week) {
            super();
            this.year = year;
            this.week = week;
        }

        public int getYear() {
            return year;
        }

        public void setYear(int year) {
            this.year = year;
        }

        public int getWeek() {
            return week;
        }

        public void setWeek(int week) {
            this.week = week;
        }

        public int compareTo(Week anotherWeek) {
            if (this.year == anotherWeek.getYear() && this.week == anotherWeek.getWeek()) {
                return 0;
            } else if (this.year > anotherWeek.getYear() || (this.year == anotherWeek.getYear() && this.week > anotherWeek.getWeek())) {
                return 1;
            } else if (this.year < anotherWeek.getYear() || (this.year == anotherWeek.getYear() && this.week < anotherWeek.getWeek())) {
                return -1;
            } else {
                return -1;
            }
        }

        /**
         * 获取当前周的第一天的日期
         *
         * @return
         */
        public Date getFirstDay() {
            Calendar calendar = getCalendar();
            calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            Date date = calendar.getTime();
            return date;
        }

        /**
         * 获取某周的最后一天的日期
         *
         * @return
         */
        public Date getLastDay() {
            Calendar calendar = getCalendar();
            calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
            return calendar.getTime();
        }

        /**
         * 获取本周的星期几是什么日期
         *
         * @param day 星期几 使用例如{@code Calendar.MONDAY...}
         * @return
         */
        public Date getDayOfWeek(int day) {
            Calendar calendar = getCalendar();
            calendar.set(Calendar.DAY_OF_WEEK, day);
            Date date = calendar.getTime();
            return date;
        }

        /**
         * 获取当前周的Calendar对象，从周一开始
         *
         * @return
         */
        private Calendar getCalendar() {
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.YEAR, year);
            calendar.set(Calendar.WEEK_OF_YEAR, this.week);
            calendar.setFirstDayOfWeek(Calendar.MONDAY);
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 1);
            return calendar;
        }

        public String toString() {
            return getYear() + "-" + getWeek();
        }
    }


}
